package org.study.tk.api.common;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.mapperhelper.MapperHelper;

import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 该类的描述
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2021-04-23
 */
public class MapperFactory {
    /**
     * 创建 mapper 实例
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T createMapper(Class<? extends Mapper> clazz) {
        SqlSession sqlSessionProxy = MapperSqlSessionFactory.getSqlSessionProxy();
        Mapper mapper = MapperSqlSessionFactory.getConfiguration().getMapper(clazz, sqlSessionProxy);
        return (T) mapper;
    }

    /**
     * 參考官网 https://mapperhelper.github.io/docs/1.integration/
     *
     * @author doveylovey
     * @version v1.0.0
     * @email 1135782208@qq.com
     * @date 2021-04-23
     */
    public static class MapperSqlSessionFactory {
        private static final String resource = "mybatis-config.xml";
        private static SqlSessionFactory sqlSessionFactory;

        static {
            // 加载配置文件
            InputStream in = MapperSqlSessionFactory.class.getClassLoader().getResourceAsStream(resource);
            // 创建 SqlSessionFactory 实例
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
            // 创建一个 MapperHelper
            MapperHelper mapperHelper = new MapperHelper();
            // 特殊配置
            Config config = new Config();
            // 主键自增回写方法，默认值MYSQL，详细说明请看文档
            config.setIDENTITY("MYSQL");
            // 支持getter和setter方法上的注解
            config.setEnableMethodAnnotation(true);
            // 设置 insert 和 update 中，是否判断字符串类型 != ''
            config.setNotEmpty(true);
            // 校验 Example 中的类型和最终调用时 Mapper 的泛型是否一致
            config.setCheckExampleEntityClass(true);
            // 启用简单类型
            config.setUseSimpleType(true);
            // 枚举按简单类型处理
            config.setEnumAsSimpleType(true);
            // 自动处理关键字 - mysql
            config.setWrapKeyword("`{0}`");
            // 设置配置
            mapperHelper.setConfig(config);
            // 注册自己项目中使用的通用 Mapper 接口，这里没有默认值，必须手动注册
            mapperHelper.registerMapper(CommonMapper.class);
            // 配置完成后，执行下面的操作
            mapperHelper.processConfiguration(sqlSessionFactory.getConfiguration());
        }

        public static Configuration getConfiguration() {
            return sqlSessionFactory.getConfiguration();
        }

        public static SqlSession getSqlSessionProxy() {
            ClassLoader classLoader = SqlSessionFactory.class.getClassLoader();
            Class<SqlSession>[] interfaces = new Class[]{SqlSession.class};
            // 方式一：新建一个实现类
            //InvocationHandler invocationHandler = new SqlSessionInterceptor();
            // 方式二：lambda 和匿名类
            InvocationHandler invocationHandler = (proxy, method, args) -> {
                Object object = null;
                try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
                    object = method.invoke(sqlSession, args);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return object;
            };

            return (SqlSession) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        }

        /*private static class SqlSessionInterceptor implements InvocationHandler {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object object = null;
                try(SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
                    object = method.invoke(sqlSession, args);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return object;
            }
        }*/
    }
}
